package com.boot.security.initialization;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.boot.database.initialization.DatabaseInitialization;
import com.boot.security.mapper.AccessMapper;
import com.boot.security.mapper.AccessResourceMapper;
import com.boot.security.mapper.ResourceMapper;
import com.boot.security.mapper.UserMapper;
import com.boot.security.model.po.*;
import com.boot.security.service.database.UserAccessService;
import com.boot.security.service.database.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * @author 霜寒 <1621856595@qq.com>
 * @description 初始化数据库
 * @date 2020/2/19 16:32
 **/
@Slf4j
@Component
public class SecurityDatabaseInitialization implements DatabaseInitialization {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserAccessService userAccessService;

    @Autowired
    private AccessMapper accessMapper;

    @Autowired
    private AccessResourceMapper accessResourceMapper;

    @Autowired
    private ResourceMapper resourceMapper;

    private final PasswordEncoder encoder = new BCryptPasswordEncoder();

    private boolean isCreateRootAccess = false;

    public UserMapper getUserMapper() {
        return userMapper;
    }

    public UserAccessService getUserAccessService() {
        return userAccessService;
    }

    public AccessMapper getAccessMapper() {
        return accessMapper;
    }

    public AccessResourceMapper getAccessResourceMapper() {
        return accessResourceMapper;
    }

    public ResourceMapper getResourceMapper() {
        return resourceMapper;
    }

    public PasswordEncoder getPasswordEncoder() {
        return encoder;
    }

    public void haveCreateRootAccess() {
        this.isCreateRootAccess = true;
    }

    public Access[] createRootAccess() {
        Access selectAll;
        Access insertAll;
        Access deleteAll;
        if (isCreateRootAccess) {
            selectAll = accessMapper.selectOne(Wrappers.query(Access.builder()
                    .code("*:select").build()));
            insertAll = accessMapper.selectOne(Wrappers.query(Access.builder()
                    .code("*:insert").build()));
            deleteAll = accessMapper.selectOne(Wrappers.query(Access.builder()
                    .code("*:delete").build()));
        } else {
            selectAll = Access.builder().code("*:select").remark("读取所有数据").readOnly(true).build();
            insertAll = Access.builder().code("*:insert").remark("创建所有数据").readOnly(true).build();
            deleteAll = Access.builder().code("*:delete").remark("删除所有数据").readOnly(true).build();
            accessMapper.insert(selectAll);
            accessMapper.insert(insertAll);
            accessMapper.insert(deleteAll);
            isCreateRootAccess = true;
        }
        return new Access[]{selectAll, insertAll, deleteAll};
    }

    public Access[] createAccess(String tableName) {
        Access selectAll = accessMapper.selectOne(Wrappers.query(Access.builder()
                .code("*:select").build()));
        Access insertAll = accessMapper.selectOne(Wrappers.query(Access.builder()
                .code("*:insert").build()));
        Access deleteAll = accessMapper.selectOne(Wrappers.query(Access.builder()
                .code("*:delete").build()));
        return createAccess(tableName, selectAll, insertAll, deleteAll);
    }

    public Access[] createAccess(String tableName, Access selectAll, Access insertAll, Access deleteAll) {
        Access select = Access.builder().code(tableName + ":select").remark("读取" + tableName + "数据").parent(selectAll.getId()).readOnly(true).build();
        Access insert = Access.builder().code(tableName + ":insert").remark("创建" + tableName + "数据").parent(insertAll.getId()).readOnly(true).build();
        Access delete = Access.builder().code(tableName + ":delete").remark("删除" + tableName + "数据").parent(deleteAll.getId()).readOnly(true).build();
        accessMapper.insert(select);
        accessMapper.insert(insert);
        accessMapper.insert(delete);
        //        selectAll.getChildren().add(select.getId());
        //        insertAll.getChildren().add(insert.getId());
        //        deleteAll.getChildren().add(delete.getId());
        return new Access[]{select, insert, delete};
    }

    public void createResource(String tableName, Access select, Access insert, Access delete) {
        Resource resource = Resource.builder().content('/' + tableName + "/id/*").method(1).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(select.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName + "/search").method(2).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(select.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName + "/list/id").method(2).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(select.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName + "/list/search").method(2).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(select.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName + "/list/like").method(2).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(select.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName + "/list").method(1).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(select.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName + "/count").method(2).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(select.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName).method(2).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(insert.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName + "/list").method(2).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(insert.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName).method(3).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(insert.getId()).resourceId(resource.getId()).readOnly(true).build());
        accessResourceMapper.insert(AccessResource.builder().accessId(delete.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName + "/list").method(3).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(insert.getId()).resourceId(resource.getId()).readOnly(true).build());
        accessResourceMapper.insert(AccessResource.builder().accessId(delete.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName).method(4).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(insert.getId()).resourceId(resource.getId()).readOnly(true).build());
        accessResourceMapper.insert(AccessResource.builder().accessId(delete.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName + "/list").method(4).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(insert.getId()).resourceId(resource.getId()).readOnly(true).build());
        accessResourceMapper.insert(AccessResource.builder().accessId(delete.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName + "/id/*").method(5).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(delete.getId()).resourceId(resource.getId()).readOnly(true).build());

        resource = Resource.builder().content('/' + tableName + "/list/id").method(5).readOnly(true).build();
        resourceMapper.insert(resource);
        accessResourceMapper.insert(AccessResource.builder().accessId(delete.getId()).resourceId(resource.getId()).readOnly(true).build());
    }

    @Override
    public void initialization() {
        log.info("Code Initialize Security...");
        User admin = User.builder().username("admin").password(encoder.encode(UserService.PRIMARY_PASSWORD)).nickname("超级管理员")
                .phone("13050101010").email("1621856595@qq.com").status(0).readOnly(true).build();
        User guest = User.builder().username("Guest").password(encoder.encode(UserService.PRIMARY_PASSWORD)).nickname("游客")
                .status(0).build();
        userMapper.insert(admin);
        userMapper.insert(guest);

        Access[] rootAccess = createRootAccess();
        Access selectAll = rootAccess[0];
        Access insertAll = rootAccess[1];
        Access deleteAll = rootAccess[2];

        String[] tableNames = new String[]{"user", "access", "resource",
                "user-access", "access-resource"};
        for (String tableName : tableNames) {
            Access[] accesses = createAccess(tableName, selectAll, insertAll, deleteAll);
            createResource(tableName, accesses[0], accesses[1], accesses[2]);
        }

        Resource resource = Resource.builder().type(1).content("/system/**").method(0).remark("前端表格展示").build();
        resourceMapper.insert(resource);
        List<Access> accesses = accessMapper.selectList(Wrappers.<Access>lambdaQuery()
                .like(Access::getCode, ":select"));
        for (Access access : accesses) {
            accessResourceMapper.insert(AccessResource.builder().accessId(access.getId()).resourceId(resource.getId()).build());
        }

        resource = Resource.builder().type(1).content("/system/access/tree").method(0).remark("权限树状图").build();
        resourceMapper.insert(resource);
        accesses = accessMapper.selectList(Wrappers.<Access>lambdaQuery()
                .in(Access::getCode, Arrays.asList("access:insert", "access:delete")));
        for (Access access : accesses) {
            accessResourceMapper.insert(AccessResource.builder().accessId(access.getId()).resourceId(resource.getId()).build());
        }

        createResource("*", selectAll, insertAll, deleteAll);

        // 因为 Access 有父子关联，所以必须放最后
        userAccessService.save(UserAccess.builder().userId(admin.getId()).accessId(selectAll.getId()).build());
        userAccessService.save(UserAccess.builder().userId(admin.getId()).accessId(insertAll.getId()).build());
        userAccessService.save(UserAccess.builder().userId(admin.getId()).accessId(deleteAll.getId()).build());
        userAccessService.save(UserAccess.builder().userId(guest.getId()).accessId(selectAll.getId()).build());
        userAccessService.update(Wrappers.<UserAccess>lambdaUpdate()
                .set(UserAccess::getReadOnly, true));
        log.info("Code Initialize Security Finish");
    }

    @Override
    public int order() {
        return 100;
    }
}
